use std::error::Error;

use serde_json::Value;
use thiserror::Error;

#[derive(Debug, Error, Clone)]
pub enum BizError {
    #[error("未查询到用户")]
    UserNotFound,

    #[error("用户被禁用")]
    UserDisabled,

    #[error("服务不存在")]
    ServiceNotFound,

    #[error("无效参数")]
    InvalidParams(Option<Value>),

    #[error("无效会话")]
    InvalidSession,

    #[error("未知异常")]
    Unknown,

    #[error("反序列化异常")]
    DeserializeError,

    #[error("数据库数据冲突.")]
    DbDuplicateErr(String),

    #[error("数据库操作异常")]
    DbErr,

    #[error("数据库操作异常")]
    DbAcquireErr,

    #[error("数据库操作异常")]
    RuntimeErr,

    #[error("数据库操作异常")]
    SqlErr,

    #[error("数据不存在")]
    DataNotFound,

    #[error("用户认证异常")]
    AuthcErr(String),

    #[error("禁止访问")]
    Forbidden,

    #[error("token 过期")]
    TokenExpired,

    #[error("token 失效")]
    TokenInvalid,

    #[error("转json异常")]
    ToJsonErr,
}

impl BizError {
    pub fn code(&self) -> String {
        let code = format!("{:?}", self);

        let index = code.find("(").unwrap_or(code.len());

        let code = &code[..index];

        code.to_string()
    }
}

/**
 * 判断是否是冲突异常
 */
pub fn is_duplicate_exception<T>(res: &anyhow::Result<T>) -> bool {
    if res.is_ok() {
        return false;
    }

    let is_dupl = res
        .as_ref()
        .err()
        .unwrap()
        .chain()
        .any(|e| e.to_string().contains("Duplicate entry"));

    is_dupl
}

pub fn parse_db_err_duplicate_err(err_msg: &str) -> Option<(String, String)> {
    let keyword = "Duplicate entry";
    let Some(s) = err_msg.find(keyword) else {
        return None;
    };
    let msg = &err_msg[s + keyword.len()..];
    let split = msg.split("for key").into_iter().collect::<Vec<_>>();
    if split.len() != 2 {
        return None;
    }

    let get_s = |s: &str| -> String {
        let Some(_index) = s.find("'") else {
            return String::new();
        };
        let s = &s[_index + 1..];
        let Some(_index) = s.find("'") else {
            return String::new();
        };

        (&s[.._index]).to_string()
    };

    let value = get_s(split[0]);
    let column = get_s(split[1]);

    Some((column.to_string(), value.to_string()))
}

#[cfg(test)]
mod tests {
    use super::{parse_db_err_duplicate_err, BizError};

    #[test]
    fn test1() {
        let err = BizError::DeserializeError;
        println!("{:?}", err.to_string());
        println!("{:?}", err);
    }

    #[test]
    fn test_parse_db_err_duplicate_err() {
        let err_msg = " error returned from database: 1062 (23000): Duplicate entry 'jaakan' for key 'tb_user.user_name'";

        let col_and_val = parse_db_err_duplicate_err(err_msg);
        println!("col_and_val:{:?}", col_and_val);
        assert_eq!(
            col_and_val,
            Some(("tb_user.user_name".to_string(), "jaakan".to_string()))
        );
    }

    fn anyhow_err() -> anyhow::Result<()> {
        Err(BizError::DbDuplicateErr("TEST".to_string()).into())
    }

    #[test]
    fn test_anyhow_err() {
        let anyhow_err = anyhow_err();
        let err = anyhow_err.err().unwrap();
        println!("{:?}", err.downcast_ref::<BizError>());
        let biz_err = err.downcast_ref::<BizError>().unwrap();

        println!("code: {:?}", biz_err.code());
        println!("{:?}", err.downcast_ref::<BizError>().unwrap());
        println!("{:?}", err.downcast_ref::<BizError>().unwrap().to_string());

        println!("code: {:?}", BizError::DataNotFound.code());
    }
}
